﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ConvexPolygon
{
    class  CheckConvexPolygon
    {
        private PointF[] points;
        private bool jordanCurve;
        private bool convexAngles;
        private PointF intersectionPoint;

        public CheckConvexPolygon(PointF[] p)
        {
            points = p;
            jordanCurve = IsJordanCurve(points, ref intersectionPoint);
            convexAngles = CheckIfAnglesLessThan180(points);
            intersectionPoint = new PointF();
        }

        public bool ConvexPolygon
        {
            get 
            {
                if (jordanCurve)
                {
                    if (convexAngles)
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
        }

        public bool JordanCurve
        {
            get { return jordanCurve; }
        }

        public bool ConvexAngles
        {
            get { return convexAngles; }
        }

        public PointF IntersectionPoint
        {
            get { return intersectionPoint; }
        }

        private static bool IsJordanCurve(PointF[] points, ref PointF  intersectionPoint)
        {
            // check if closed curve
            if (points[0] != points[points.Length - 1])
            {
                Console.WriteLine("Not JordantCurve - curve not closed");
                return false;
            }
            if (points.Length < 4)
            {
                return false;
            }
            for (int i = 0; i < points.Length - 1; i++)
            {
                for (int j = i + 2; j < points.Length - 1; j++)
                {
                    //check if intersects
                    intersectionPoint = Intersection(points[i], points[i + 1], points[j], points[j + 1]);
                    if (!intersectionPoint.IsEmpty)
                    {
                        Console.WriteLine("Not JordantCurve - intersection detected");
                        return false;
                    }
                }
            }
            return true;
        }

        private static PointF Intersection(PointF l10, PointF l11, PointF l20, PointF l21)
        {

            double x0, y0, x1, y1, x2, y2, x3, y3;
            double xi, yi;
            double a1, b1, c1, a2, b2, c2, det_inv;  // constants of linear equations
            double m1, m2;                           // the slopes of each line

            PointF rez = new PointF();

            x0 = (double)l10.X;
            y0 = (double)l10.Y;
            x1 = (double)l11.X;
            y1 = (double)l11.Y;
            x2 = (double)l20.X;
            y2 = (double)l20.Y;
            x3 = (double)l21.X;
            y3 = (double)l21.Y;
            
            // check for vertical lines
            if (x1 - x0 == 0 || x3 - x2 == 0)
            {
                if (x1 - x0 == 0 && x3 - x2 == 0)
                {
                    // both vertical lines
                    return new PointF();
                }
                else
                {
                    //check if x ranges overlaps
                    if (Math.Min(x0, x1) <= Math.Max(x2, x3) && Math.Max(x0, x1) >= Math.Min(x2, x3))
                    {
                        //check if y ranges overlaps
                        if (Math.Min(y0, y1) <= Math.Max(y2, y3) && Math.Max(y0, y1) >= Math.Min(y2, y3))
                        {
                            //calculate intersection point
                            LineEquation eq;
                            if (x1 - x0 == 0)
                            {
                                eq = new LineEquation(new PointF((float)x2, (float)y2), new PointF((float)x3, (float)y3));
                                xi = x0;
                                yi = eq.SolveY(xi);
                            }
                            else
                            {
                                eq = new LineEquation(new PointF((float)x0, (float)y0), new PointF((float)x1, (float)y1));
                                xi = x2;
                                yi = eq.SolveY(xi);
                            }
                        }
                        else
                            return new PointF();
                    }
                    else
                        return new PointF();
                }
            }
            else
            {
                m1 = (y1 - y0) / (x1 - x0);
                m2 = (y3 - y2) / (x3 - x2);

                a1 = m1;
                a2 = m2;

                b1 = -1;
                b2 = -1;

                c1 = (y0 - m1 * x0);
                c2 = (y2 - m2 * x2);

                // compute the inverse of the determinate
                det_inv = 1 / (a1 * b2 - a2 * b1);

                // intersection point
                xi = ((b1 * c2 - b2 * c1) * det_inv);
                yi = ((a2 * c1 - a1 * c2) * det_inv);
            }

            //check if intersection belongs to segments, but not to the ends of them
            if (xi <= Math.Max(x0, x1) && xi >= Math.Min(x0, x1) && xi <= Math.Max(x2, x3) && xi >= Math.Min(x2, x3))
            {
                
                double roundedX = Math.Round(xi);
                double roundedY = Math.Round(yi);
                if (Math.Abs(roundedX - xi) < 0.00000001)
                    xi = roundedX;
                if (Math.Abs(roundedY - yi) < 0.00000001)
                    yi = roundedY;

                PointF temp = new PointF((float)xi, (float)yi);
                
                // check if doesn't belong to bow segment ends
                if (!((temp.Equals((PointF)l10) || temp.Equals((PointF)l11)) && (temp.Equals((PointF)l20) || temp.Equals((PointF)l21))))
                {
                    Console.WriteLine("Intersection point: " + xi + ", " + yi);
                    rez.X = (float)xi;
                    rez.Y = (float)yi;
                }
            }

            return rez;
        }
        
        private static bool CheckIfAnglesLessThan180(PointF[] points)
        {
            int sign; // -1 = "-", 1 = "+"
            sign = CrossProductSign(points[points.Length-2], points[0], points[1]);
            
            for (int i = 1; i < points.Length - 1; i++)
            {
                if (sign != CrossProductSign(points[i-1], points[i], points[i+1]))
                {
                    Console.WriteLine("Different sign detected!");
                    return false;
                }
            }
            return true;
        }

        private static int CrossProductSign(PointF p0, PointF p1, PointF p2)
        {
            // curve: (x0,y0) <-> (x1,y1) <-> (x2,y2)
            // point of angle: (x1,y1)
            double crossProduct;
            double x0, x1, x2, y0, y1, y2;

            x0 = (double)p0.X;
            x1 = (double)p1.X;
            x2 = (double)p2.X;
            y0 = (double)p0.Y;
            y1 = (double)p1.Y;
            y2 = (double)p2.Y;

            crossProduct = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
            return Math.Sign(crossProduct);
        }
    }
    

}
